﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using MadMilkman.Ini;
using MdcService.Static;

namespace MdcService.Configuration
{
    public class IniConfigurationManager:IConfigurationManager<Configuration>
    {
       

        private const string SettingIniFilePath = "MdcService.ini";
        private const string CryptString = null; //"(ง •̀_•́)ง";
        private readonly IniOptions _iniOptions = new IniOptions
        {
            Encoding = Encoding.UTF8,
            EncryptionPassword = CryptString
        };

        private Configuration configuration;

        private string[][] _settingFields;
        private IEnumerable<string[]> SettingFields
        {
            get
            {
                return _settingFields ?? (_settingFields = typeof(Configuration).GetProperties()
                           .Select(i => new { prop = i, Attr = i.GetCustomAttribute<SaveAttribute>() })
                           .Where(i => i.Attr != null)
                           .Select(
                               i => new[] { i.Attr.SectionName, i.Attr.FieldName, Convert.ToString(i.prop.GetValue(configuration)) })
                           .GroupBy(i => i[0])
                           .SelectMany(i => i.Select(j => new[] { j[0], j[1], j[2] }))
                           .ToArray());
            }
        }


        private string[] _settingSections;
        private IEnumerable<string> SettingSections
        {
            get
            {
                return _settingSections ?? (_settingSections = typeof(Configuration).GetProperties()
                           .Select(i => new { i, Attribute = i.GetCustomAttribute<SaveAttribute>() })
                           .Where(i => i.Attribute != null)
                           .GroupBy(i => i.Attribute.SectionName)
                           .Select(i => i.Key)
                           .ToArray());
            }
        }

        public Configuration Load()
        {
            var f = new IniFile(_iniOptions);
            if (!File.Exists(SettingIniFilePath))
            {
                Save(new Configuration());
            }
            f.Load(SettingIniFilePath);
            configuration = new Configuration();
           

            SettingFields.ForEach(i =>
            {
                if (!f.Sections.Contains(i[0]) || !f.Sections[i[0]].Keys.Contains(i[1])) return;
                var p = configuration.GetType().GetProperty(i[1]);
                var oldvalue = f.Sections[i[0]].Keys[i[1]].Value;
                var newvalue = Convert.ToString(p?.GetValue(configuration));
                if (oldvalue == newvalue) return;
                p?.SetValue(configuration, Convert.ChangeType(f.Sections[i[0]].Keys[i[1]].Value, p.PropertyType));
            });
            return configuration;
        }

        public void Save(Configuration configuration)
        {
            this.configuration = configuration;
            var f = new IniFile(_iniOptions);
            if (File.Exists(SettingIniFilePath))
            {
                f.Load(SettingIniFilePath);
            }
            SettingSections.ForEach(i =>
            {
                if (!f.Sections.Contains(i)) f.Sections.Add(i);
            });
            SettingFields.ForEach(i =>
            {
                if (!f.Sections[i[0]].Keys.Contains(i[1]))
                {
                    f.Sections[i[0]].Keys.Add(i[1], i[2]);
                }
                else
                {
                    var oldvalue = f.Sections[i[0]].Keys[i[1]].Value;
                    if (i[2] == oldvalue) return;
                    f.Sections[i[0]].Keys[i[1]].Value = i[2];
                }
            });
            f.Save(SettingIniFilePath);
        }
    }
}
